package com.ds.infra.rpc.core.transport.codec;

import com.ds.infra.rpc.core.constant.RpcConstants;
import com.ds.infra.rpc.core.protocol.RpcProtocol;
import com.ds.infra.rpc.core.serializer.SerializerFactory;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;

import java.nio.ByteOrder;
import java.util.List;

/**
 * 泛型化解码器（支持RpcRequest/RpcResponse）
 *
 * @author WeiShaoying
 */
@Slf4j
public class RpcDecoder<T> extends ByteToMessageDecoder {

    private final Class<T> clazz;

    public RpcDecoder(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        // 1. 检查最小长度（魔数2B + 版本1B + 类型1B + 长度4B = 8B）
        if (in.readableBytes() < 8) {
            return;
        }
        // 2. 标记读取位置（支持回退）
        in.markReaderIndex();
        // 3. 大端序读取魔数
        short magic = in.order(ByteOrder.BIG_ENDIAN).readShort();
        if (magic != RpcConstants.MAGIC) {
            log.error("Invalid magic number: 0x{} (expected: 0xCAFE)",
                    Integer.toHexString(magic & 0xFFFF));
            in.resetReaderIndex();
            ctx.close();
            return;
        }

        // 4. 读取协议头剩余部分
        byte version = in.readByte();
        byte msgType = in.readByte();
        int bodyLength = in.readInt();

        // 5. 检查消息体完整性
        if (in.readableBytes() < bodyLength) {
            in.resetReaderIndex();
            return;
        }

        // 6. 反序列化消息体
        try {
            byte[] bodyBytes = new byte[bodyLength];
            in.readBytes(bodyBytes);
            T body = SerializerFactory.deserialize(bodyBytes, clazz);

            // 7. 构建完整协议对象
            RpcProtocol<T> protocol = new RpcProtocol<>(
                    new RpcProtocol.Header(magic, version, msgType, bodyLength),
                    body
            );
            out.add(protocol);
            log.debug("Decoded {}: magic=0xCAFE, type={}", clazz.getSimpleName(), msgType);
        } catch (Exception e) {
            log.error("Decode failed", e);
            ctx.close();
        }
    }
}
